use super::ByteResult;

macro_rules! read_byte_impl {
    (impl,$($data:ty,$func:ident),*) => {
        $(
            pub fn $func(&mut self) -> ByteResult<$data> {
                self.inner.$func()
            }
        )*
    };
    (trait,$($data:ty,$func:ident),*) => {
        $(
            fn $func(&mut self) -> ByteResult<$data> {
                let mut buf = [0u8;std::mem::size_of::<$data>()];
                self.read_exact(&mut buf)?;
                Ok(<$data>::from_le_bytes(buf))
            }
        )*
    };
}

pub struct ByteReader<R> {
    inner:R,
}

impl<R> ByteReader<R> {
    pub fn into_inner(self) -> R {
        self.inner
    }
}

impl<R:ByteRead> ByteReader<R> {
    pub fn new(inner:R) -> Self {
        Self { inner }
    }

    read_byte_impl!{
        impl,
        u8,read_to_u8,
        u16,read_to_u16,
        u32,read_to_u32,
        u64,read_to_u64,
        u128,read_to_u128
    }

    pub fn read_to_bytes(&mut self,bytes:&mut [u8]) -> ByteResult<()> {
        self.inner.read_to_bytes(bytes)?;
        Ok(())
    }
}

pub trait ByteRead {
    type Error;
    fn read_to_u8(&mut self) -> ByteResult<u8>;
    fn read_to_u16(&mut self) -> ByteResult<u16>;
    fn read_to_u32(&mut self) -> ByteResult<u32>;
    fn read_to_u64(&mut self) -> ByteResult<u64>;
    fn read_to_u128(&mut self) -> ByteResult<u128>;
    fn read_to_bytes(&mut self,bytes:&mut [u8]) -> ByteResult<()>;
}

impl<R:std::io::Read> ByteRead for R {
    type Error = std::io::Error;
    read_byte_impl!{
        trait,
        u8,read_to_u8,
        u16,read_to_u16,
        u32,read_to_u32,
        u64,read_to_u64,
        u128,read_to_u128
    }

    fn read_to_bytes(&mut self,bytes:&mut [u8]) -> ByteResult<()>  {
        self.read_exact(bytes)?;
        Ok(())
    }
}